package com.example.security_demo.sms;

import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;

import java.util.ArrayList;
import java.util.stream.Collectors;

/**
 * 自定义 认证 Provider
 *
 * @author zzh
 * @date 2022/4/26
 * @since 1.0
 */
public class SmsAuthenticationProvider implements AuthenticationProvider {
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Assert.isInstanceOf(SmsAuthenticationToken.class, authentication,
                "Only SmsAuthenticationToken is supported");
        SmsAuthenticationToken token = ((SmsAuthenticationToken) authentication);
        String smsCode = token.getSmsCode();
        String  mobile = ((String) token.getPrincipal());
        // 在这里对验证码以及手机号进行验证
        if (!checkCode(smsCode)) {
            throw new UsernameNotFoundException("验证码错误");
        }
        // 如果验证码正确便可以通过电话号码到数据库或缓存中获取用户的信息，在这里需要引入一个新的概念
        // UserDetails 和 权限的概念
        System.out.println(mobile);
        ArrayList<String> rawList = new ArrayList<>();
        rawList.add("role_user");
        rawList.add("role_admin");
        return new SmsAuthenticationToken(mobile, rawList.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()));
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsAuthenticationToken.class.isAssignableFrom(authentication);
    }

    /**
     * 我在这里做了简化的处理，正常情况下应该是在session 或者  redis 等缓存中获取比对
     * 同时一定要注意： 如果是通过session，一定要把验证流程放到过滤器中，因为在provider对象中
     * 注入 request对象会比较难以处理，可以放到 SmsAuthenticationFilter中验证也可以重新定义一个
     * filter.
     * @param smsCode 短信验证码
     * @return 如果验证码正确返回true 反之返回false
     */
    private boolean checkCode(String smsCode) {
        return smsCode != null && smsCode.length() > 0;
    }
}
